JavaScript 函数 闭包

函数

函数是一块JavaScript代码,被定义一次,但可执行和调用多次。 JS中的函数也是对象,所以JS函数可以像其它对象那样操作和传递 所以我们也常叫JS中的函数为函数对象。

重点:

- this
- arguments
- 作用域
- 不同 的调用方法
- 不同 的创建方法
```
### 不同的调用方法:
  • 直接调用 foo();
  • 对象方法 obj.method();
  • 构造器 new Foo();
  • call/apply/bind , func.call(obj);
    ### 函数声明 Vs. 函数表达式
    函数声明
    ```javascript
    function add (a, b) {
    truea = +a;
    trueb = +b;
    if (isNaN(a) || isNaN(b)) { return;
    true}
    truereturn a + b;
    }

函数表达式

// function variable 最常用
var add = function (a, b) {
true// do sth
};
// IEF(Immediately Executed Function) 经常用
(function() {
true// do sth
})();
// first-class function 很少用
return function() {
true// do sth
};
// NFE (Named Function Expression) 很少用,递归的时候会用到
var add = function foo (a, b) {
true// do sth
};

变量与函数的声明前置

var num=add(1,2) //3
console.log(num);
function add(a, b){
truea=+a;
trueb=+b;
trueif(isNaN(a) || isNaN(b)){
truetruereturn;
true}
truereturn a+b;
}

Notes: 函数表达式方式这么做是错的.

命名函数表达

var func = function nfe() {};
alert(func === nfe);
// 递归调用
var func = function nfe() {
true/** do sth.**/
truenfe();
}

Function构造器

var func = new Function('a', 'b', 'console.log(a + b);');
func(1, 2); // 3
var func = Function('a', 'b', 'console.log(a + b);');
func(1, 2); // 3
//CASE1 localVal 仍为局部变量
Function('var localVal="local";console.log(localVal);')();
console.log(typeof localVal);
//result: local,undefined
//CASE2 local 不可访问,全局变量global可以访问 .原因,想想闭包
var globalVal='global';
(function(){
var localVal='local';
Function('console.log(typeof localVal,typeof globalVal);')();
})();

this

全局的this (浏览器) ,指向window/浏览器.

console.log(this.document  ===  document);  //  true  
console.log(this  ===  window);  //  true  
this.a  =  37;   console.log(window.a);  //  37

一般函数的this(浏览器),指向浏览器/window.

function  f1(){      
return  this;  
}
f1()  ===  window;  //  true,  global  object
function  f2(){
true"use  strict";  //  see  strict  mode
truereturn  this;
}  
f2()  ===  undefined;  //  true

作为对象方法的函数的this,指向所创建的对象

var o = {
trueprop: 37,
truef: function() {
truetruereturn this.prop;
true}
};
console.log(o.f()); // logs 37
var o = {prop: 37};
function independent() {
truereturn this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37

对象原型链上的this

指向最具体层的对象,虽然在这里是this是p的原型链p上的this,创建过程中指向了p

var o = {
truef: function(){
truetruereturn this.a + this.b;
true}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5

get/set方法与this 指向get/set的对应的对象

function modulus(){
truereturn Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
truere: 1,
trueim: -1,
trueget phase(){
truetruereturn Math.atan2(this.im, this.re);
true}
};
Object.defineProperty(o, 'modulus', {
trueget: modulus, enumerable:true, configurable:true});
trueconsole.log(o.phase, o.modulus); // logs -0.78 1.4142

构造器中的this

function  MyClass(){
truethis.a  =  37;
}  
var  o  =  new  MyClass();
//new 若没有返回值或返回值为基本类型,则将this作为返回值,返回给新创建的对象
console.log(o.a);  
//  37,来资源原型链中查找到,而不是自身的
function  C2(){
truethis.a  =  37;
truereturn  {a  :  38};
}  
/**
当我们使用new(作为构造器)来创建对象的时候,
这时this会指向一个空的对象,这个空对象的原型会指向是 Myclass.porotary
this会指向 MyClass.porototype的空对象
但是当有返回值时,如果返回的是个对象的话,那么会把对象作为返回值,所以这里o.a =38,这里的o的this指向返回的对象{a:38},所以o.a=38
*/
o  =  new  C2();
console.log(o.a);  //  38

call/apply方法与this

function  add(c,  d){
truereturn  this.a  +  this.b  +  c  +  d;
}  
var  o  =  {a:1,  b:3};  
add.call(o,  5,  7);  //  1  +  3  +  5  +  7  =  16  
add.apply(o,  [10,  20]);  //  1  +  3  +  10  +  20  =  34  
function  bar()  {
trueconsole.log(Object.prototype.toString.call(this));
}  
bar.call(7);  //  "[object  Number]
/**
* Notes:
* call(this,arg[0],arg[1],...);我们用这个函数去调用没法直接调用的方法
*/

bind方法与this

作用同call/apply,更简单高效ES5支持

function f(){
truereturn this.a;
}
var g = f.bind({a : "test"});
console.log(g()); // test
var o = {a : 37, f : f, g : g};
console.log(o.f(), o.g()); // 37, test

参数属性与arguments

function foo(x,y) {
trueconsole.log(x,y,this);
}
foo.call(100,1,2);
//1,2,Number(100)
foo.apply(true,[3,4]);
//3,4,Boolean(true)
foo.apply(null);
//undefined,undefined,window (注意第三个量)
foo.apply(undefined);
//undefined,undefined,window (注意第三个量)

apply/call 方法(浏览器)

function foo(x,y){
'use sttic';
console.log(x,y,this);
}
foo.apply(null); // undefined, undefined, null
foo.apply(undefined); // undefined, undefined, undefined

bind方法

this.x=9;
var module={
truex:81,
truegetX: function(){
truetruereturn this.x;
true}
};
module.getX();//81
var getX = module.getX;
getX();//9
var boundGetX=getX.bind(module);
boundGetX();//81

bing与currying

function add(a,b,c){
return a+b+c;
}
var func=add.bind(undefined,100);
func(3);//103
var func2=func.bind(undefined,200);
func2(10);//310
function getConfig(colors,size,otherOptions){
trueconsole.log(colors,size,otherOptions);
}
var defaultConfig=getConfig.bind(null,"#CC0000","1024*768");
defaultConfig("123");
degaultConfig("456");

bind 与 new

function foo(){
this.b=100;
return this.a;
}
var func = foo.bind({a:1});
func();
//1,这里返回1,并且函数对象获得一个b=100的属性,但不作为返回值
new func();
//{b:100}。new之后会有一个空对象,空对象的原型会指向foo.prototype.这样一个空对象的b属性会被设置为b=100,整个空对象作为返回值返回,忽略掉return this.a的一个return.用new调用,对于这种this的情况,会被忽略掉.

闭包

!function(){
truevar localData="localData here";
truedocument.addEventListener('click',
truefunction(){
truetrueconsole.log(localData);
true});
}();
!function(){
truevar localData="localData here";
truevar url="http://www.wanglinzhizhi.me/";
true$.ajax({
truetrueurl:url,
truetruesucess:function(){
truetruetrue//do sth...
truetruetrueconsole.log(localData);
truetrue}
true});
}();

闭包常见错误之循环闭包

document.body.innerHTML="<div id=div1>aa</div>"+"<div id=div2>bbb</div><div id=div3>ccc</div>";
for(var i=0;i<4;i++){
document.getElementById('div'+i).addEventListener('click',function(){
alert(i);//all are 4
true});
}

改正如下:

document.body.innerHTML="<div id=div1>aa</div>"
+"<div id=div2>bbb</div><div id=div3>ccc</div>";
for(var i=1;i<4;i++){
!function(i){
document.getElementById('div'+i).addEventListener('click',function(){
alert(i);//1,2,3
});
}(i);
};

闭包-封装

!function(){
var _userId=2345;
var _typeId='item';
var export={};
function converter(userId){
return +userId;
}
export.getUserId=function(){
return converter(_userId);
}
export.getTypeId=function(){
return _typeId;
}
window.export=export;
}());
export.getUserId();//2345
export.getTypeId();//item
export._userId; //undefined
export._typeId; //undefined
export.converter;//undefined

闭包的概念

在计算机科学中,闭包(也称词法闭包或函数闭包)是指一个函数或函数的引用,与一个引用环境绑定在一起。这 个引用环境是一个存储该函数每个非局部变量(也叫自由变量)的表。
闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量。
from 维基百科

闭包-小结

优点

  • 灵活和方便
  • 封装

缺点

  • 空间浪费
  • 内存泄漏
  • 性能消耗

作用域

作用域(全局、函数、eval)

var a = 10; //全局
(function() {
truevar b = 20; //函数
})();
console.log(a); // 10
console.log(b); // error, b in not defined
for (var item in {a : 1, b : 2}) {
trueconsole.log(item);
}
console.log(item); // item still in scope
//eval("var a = 1");

作用域链

function outer2(){
truevar local2=1;
truefunction outer1(){
truetruevar local1=1;
truetrue//visit local1,local2,or global3
truetrueconsole.log(local1, local2, global3);
true}
trueouter1();
}
var global3=1;
outer2();
function outer(){
truevar i=1;
truevar func=new Function("console.log(typeof i);");
truefunc();//undefined
}
outer();

利用函数作用域封装

(function(){
//to do sth here
var a,b;
})();
!function(){
var a,b;
}();

个人心得:
以前在学C++,Java的时候没觉得面向对象怎么怎么样,觉得继承也好,重载也好,就应该是那样的,没什么了不起的.现在学Javascript感慨JavaScript是彻头彻尾的面向对象啊(嗯?哦,我知道它是函数式啦),各种设计模式满天飞啊~~

ES6/7 Balel太提高生产力了